package com.example.webviewtest;

import android.os.AsyncTask;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ModbusTcpClient {

    private static final String TAG = "ModbusTcpClient";
    private static final int UNIT_ID = 1; // 设备地址
    private static final int SERVER_PORT = 502; // Modbus/TCP默认端口
    private static final String SERVER_IP = "192.168.1.106"; // PDU插排设备的IP地址

    public interface OnDataReceivedListener {
        void onDataReceived(int[] registerValues);
    }

    private OnDataReceivedListener mListener;

    public ModbusTcpClient(OnDataReceivedListener listener) {
        mListener = listener;
    }

    public void readHoldingRegisters(final int registerAddress, final int numberOfRegisters) {
        new AsyncTask<Void, Void, int[]>() {
            @Override
            protected int[] doInBackground(Void... voids) {
                try {
                    Socket socket = new Socket(SERVER_IP, SERVER_PORT);
                    OutputStream out = socket.getOutputStream();
                    InputStream in = socket.getInputStream();

                    // 构建请求报文
                    byte[] request = buildReadHoldingRegistersRequest(UNIT_ID, registerAddress, numberOfRegisters);

                    // 发送请求
                    out.write(request);
                    out.flush();

                    // 接收响应
                    byte[] response = receiveResponse(in);

                    // 解析响应
                    int[] registerValues = parseReadHoldingRegistersResponse(response);
                    return registerValues;

                } catch (IOException e) {
                    Log.e(TAG, "Error during Modbus/TCP communication", e);
                }
                return null;
            }

            @Override
            protected void onPostExecute(int[] registerValues) {
                if (mListener != null && registerValues != null) {
                    mListener.onDataReceived(registerValues);
                }
            }
        }.execute();
    }

    public void writeSingleCoil(final int coilAddress, final boolean value) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                try {
                    Socket socket = new Socket(SERVER_IP, SERVER_PORT);
                    OutputStream out = socket.getOutputStream();
                    InputStream in = socket.getInputStream();

                    // 构建请求报文
                    byte[] request = buildWriteSingleCoilRequest(UNIT_ID, coilAddress, value);

                    // 发送请求
                    out.write(request);
                    out.flush();

                    // 接收响应
                    byte[] response = receiveResponse(in);

                    // 解析响应
                    // 这里可以解析响应数据，但通常写单个线圈不需要返回数据

                } catch (IOException e) {
                    Log.e(TAG, "Error during Modbus/TCP communication", e);
                }
                return null;
            }
        }.execute();
    }

    private byte[] buildReadHoldingRegistersRequest(int unitID, int registerAddress, int numberOfRegisters) {
        ByteBuffer buffer = ByteBuffer.allocate(12);
        buffer.order(ByteOrder.BIG_ENDIAN);
        buffer.put((byte) 0x12); // Transaction ID (not used by Modbus/TCP)
        buffer.put((byte) 0x34); // Transaction ID (not used by Modbus/TCP)
        buffer.put((byte) 0x00); // Protocol ID (always 0 for Modbus/TCP)
        buffer.put((byte) 0x00); // Protocol ID (always 0 for Modbus/TCP)
        buffer.put((byte) 0x00); // Protocol ID (always 0 for Modbus/TCP)
        buffer.put((byte) 0x06); // Protocol ID (always 0 for Modbus/TCP)
//        buffer.putShort((short) (7 + 2 * numberOfRegisters)); // Length (7 bytes header + 2 * numberOfRegisters bytes data)
        buffer.put((byte) unitID); // Unit ID
        buffer.put((byte) 0x03); // Function code (Read Holding Registers)
        buffer.putShort((short) registerAddress); // Register address
        buffer.putShort((short) numberOfRegisters); // Number of registers

        return buffer.array();
    }

    private byte[] buildWriteSingleCoilRequest(int unitID, int coilAddress, boolean value) {
        ByteBuffer buffer = ByteBuffer.allocate(9);
        buffer.order(ByteOrder.BIG_ENDIAN);
        buffer.put((byte) 0x00); // Transaction ID (not used by Modbus/TCP)
        buffer.put((byte) 0x00); // Transaction ID (not used by Modbus/TCP)
        buffer.put((byte) 0x00); // Protocol ID (always 0 for Modbus/TCP)
        buffer.put((byte) 0x00); // Protocol ID (always 0 for Modbus/TCP)
        buffer.putShort((short) 7); // Length (7 bytes header)
        buffer.put((byte) unitID); // Unit ID
        buffer.put((byte) 0x05); // Function code (Write Single Coil)
        buffer.putShort((short) coilAddress); // Coil address
        buffer.putShort(value ? (short) 0xFF00 : (short) 0x0000); // Coil value (true or false)

        return buffer.array();
    }

    private byte[] receiveResponse(InputStream in) throws IOException {
        byte[] header = new byte[7];
        in.read(header);

        ByteBuffer buffer = ByteBuffer.wrap(header);
        buffer.order(ByteOrder.BIG_ENDIAN);

        short length = buffer.getShort(6);
        byte[] data = new byte[length];
        in.read(data);

        return data;
    }

    private int[] parseReadHoldingRegistersResponse(byte[] response) {
        if (response.length < 2) {
            throw new IllegalArgumentException("Invalid response length");
        }

        ByteBuffer buffer = ByteBuffer.wrap(response);
        buffer.order(ByteOrder.BIG_ENDIAN);

        int byteCount = buffer.get(); // Byte count
        int[] registerValues = new int[(response.length - 1) / 2];

        for (int i = 0; i < registerValues.length; i++) {
            registerValues[i] = buffer.getShort() & 0xFFFF; // Unsigned short
        }

        return registerValues;
    }
}